#include "CenterCache.h"

CenterCache CenterCache::_sInst;

Span* CenterCache::BatchOneSpan(SpanList& spanList, size_t size)
{
	//1、假如当前的spanList中含有对应的span，直接获取即可,如果当前结点的链表结点不够预期申请的结点呢，我们threadCache最初只是申请一个
	// 预期给n个减少申请，但是我们给一个同样可以。
	Span* curSpan = spanList.Begin();
	while (curSpan != spanList.End())
	{
		if (curSpan->_freeList != nullptr)  //当前span含有空闲结点,但是span的freeList可能不够我们预期申请的数量
		{
			return curSpan;  
		}
		else
		{
			curSpan = curSpan->_next;
		}
	}
	spanList._lock.unlock();
	//2、当前spanList不含有对应的span，需要向上级pangeCache获取对应的Span
	// (1)span要开多大空间
	
	PageCache::GetInst()._lock.lock();
	Span* span = PageCache::GetInst().NewSpan(Size::NumMovePage(size));
	PageCache::GetInst()._lock.unlock();

	//技算当前span的起始地址和终止地址。
	size_t spanSizes = span->_pageCount << PAGE_SHIFT;
	char* start = (char*)(span->_curPage << PAGE_SHIFT);
	char* end = start + spanSizes;


	//3、得到空间之后对空间进行切割
	span->_freeList = start;
	char* tail = start;
	//NextSite(start) = nullptr;//严谨
	start += size;

	while (start < end)
	{
		NextSite(tail) = start;
		tail = start;
		start += size;
	}

	NextSite(tail) = nullptr;
	//这里如果最后一个只剩下一点点，会不会有一点内存无法挂在自由链表上呢。

	//4、将做好的span挂接回centerCache
	spanList._lock.lock();
	spanList.push_front(span);

	return span;
}


//获取对应的span, 申请一定数量的结点分配给threadCache
size_t CenterCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
	size_t index = Size::index(size);
	_spanList[index]._lock.lock();
	//由于是多线程申请内存，所以我们要加锁
	Span* span = BatchOneSpan(_spanList[index], size);
	assert(span != nullptr);
	assert(span->_freeList != nullptr);
	
	//输出型参数可以得到一段链表结点。
	start= span->_freeList;
	end = span->_freeList;
	int actualSize = 1;
	for (size_t i = 0; ((i < batchNum - 1)&& (NextSite(end) != nullptr)); i++)
	{
		end = NextSite(end);
		actualSize += 1;
	}
	//将得到的链表结点从中心储存的span中去掉
	span->_freeList = NextSite(end);
	NextSite(end) = nullptr;
	span->_allocNum += actualSize;
	_spanList[index]._lock.unlock();
	return actualSize;
}


void CenterCache::RecycleToCenterStorage(void* start, size_t index)
{
	// _recycle_lock.lock();
	// 		recycleTimes += 1;
	// _recycle_lock.unlock();

	_spanList[index]._lock.lock();

	while (start)
	{
		void* next = NextSite(start);
		Span* span = PageCache::GetInst().SiteToSpan(start);
		NextSite(start) = span->_freeList;
		span->_freeList = start;
		span->_allocNum -= 1;


		if (span->_allocNum == 0)
		{
			_spanList[index].erase(span);
			span->_next = nullptr;
			span->_prev = nullptr;
			span->_freeList = nullptr;

			//这里可以释放掉桶锁，因为当前span已经从桶中移除
			_spanList[index]._lock.unlock();

			PageCache::GetInst()._lock.lock();
			PageCache::GetInst().RecycleToPageStorage(span);
			PageCache::GetInst()._lock.unlock();

			_spanList[index]._lock.lock();
		}
		start = next;
	}
	_spanList[index]._lock.unlock();
}